﻿using Blazored.LocalStorage;
using Iot.Models;
using Microsoft.AspNetCore.Components.Authorization;
using Microsoft.IdentityModel.Tokens;
using Microsoft.JSInterop;
using Newtonsoft.Json.Linq;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;

namespace Iot.Services
{
    public interface IAuthenticationService
    {
        Task<TokenViewModel> SignInAsync(LoginViewModel model);
        Task SignOutAsync();
    }

    public class AuthenticationService : AuthenticationStateProvider, IAuthenticationService
    {
        private readonly ILocalStorageService _localStorageService;
        private readonly ILogger<AuthenticationService> _logger;

        private const string SECRETKEY = "f9537ee4-88a1-4fca-b19b-4004c72f2854";
        private const string ISSUER = "ISSUER";
        private const string AUDIENCE = "AUDIENCE";

        public AuthenticationService(IServiceProvider serviceProvider)
        {
            _localStorageService = serviceProvider.GetRequiredService<ILocalStorageService>();
            _logger = serviceProvider.GetRequiredService<ILogger<AuthenticationService>>();
        }

        public override async Task<AuthenticationState> GetAuthenticationStateAsync()
        {
            //await Task.Delay(5000);

            ClaimsPrincipal principal = new();
            var token = await _localStorageService.GetItemAsStringAsync("token");

            if (!string.IsNullOrWhiteSpace(token))
            {
                //校验并解析TOKEN数据
                try
                {
                    var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(SECRETKEY));
                    var validateParameter = new TokenValidationParameters()
                    {
                        ValidateIssuer = true,
                        ValidateAudience = true,
                        ValidateIssuerSigningKey = true,

                        ValidIssuer = ISSUER,
                        ValidAudience = AUDIENCE,
                        IssuerSigningKey = securityKey,

                        ClockSkew = TimeSpan.Zero//校验过期时间必须加此属性
                    };
                    var temp = new JwtSecurityTokenHandler().ValidateToken(token, validateParameter, out SecurityToken validatedToken);
                    var claims = new List<Claim>();
                    claims.AddRange(temp.Claims);
                    claims.Add(new Claim(ClaimTypes.Authentication, token));

                    var identity = new ClaimsIdentity(claims, "api");
                    principal.AddIdentity(identity);
                }
                catch (SecurityTokenExpiredException ex)
                {
                    _logger.LogError(ex.Message);
                }
                catch (SecurityTokenException ex)
                {
                    _logger.LogError(ex.Message);
                }
                catch (Exception ex)
                {
                    _logger.LogError(ex.Message);
                }
            }

            return new(principal);
        }

        public async Task<TokenViewModel> SignInAsync(LoginViewModel model)
        {
            TokenViewModel apiResult = new();
            if (model.UserName == AppData.AppConfig.Username && model.Password == AppData.AppConfig.Password)
            {
                //JWT:  Header.Payload.Credential

                //1.Header
                var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(SECRETKEY));
                var header = new JwtHeader(new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256));

                //2.Payload
                var userClaims = new List<Claim>
            {
                new(ClaimTypes.Sid, Guid.NewGuid().ToString()),
                new(ClaimTypes.NameIdentifier, model.UserName!)
            };
                var payload = new JwtPayload(
                        issuer: ISSUER,
                        audience: AUDIENCE,
                        userClaims,
                        notBefore: null,
                        expires: DateTime.Now.AddMonths(1)
                    );

                //3.Credential
                var token = new JwtSecurityToken(header, payload);

                var handler = new JwtSecurityTokenHandler();
                string jwtToken = handler.WriteToken(token);

                await _localStorageService.SetItemAsStringAsync("token", jwtToken);

                apiResult.Token = jwtToken;
                apiResult.Message = "SUCCESS";
                var auth = GetAuthenticationStateAsync();

                NotifyAuthenticationStateChanged(auth);
            }

            return apiResult;
        }

        public async Task SignOutAsync()
        {
            await _localStorageService.RemoveItemAsync("token");
            var auth = GetAuthenticationStateAsync();
            NotifyAuthenticationStateChanged(auth);
        }

    }
}
